﻿<#
.DESCRIPTION
    Get a list of database name on a particular server/instance.
.PARAMETER computers
    An array of computers for which perfmon counters should be collected.
.PARAMETER computersListFilePath
    A file containing a list of computers for which perfmon counters should be collected.
.PARAMETER counters
    The paths of the counters that should be collected.
.PARAMETER templates
    The name of the template that countains the list of counters to be collected.
.PARAMETER outputType
    Specify "log" if you want to have the perfmon counter samples output to a file or stream if you want to send them to the PowerShell pipeline.
$PARAMETER tag 
    A tag that will prefix the output file name.
.NOTES
    Author: Mathieu Isabel   
#>
Function Get-PerformanceMonitoring($computers,$computersListFilePath,$counters,$templates,$outputType,$tag,$logFilePath,$sampleInterval,$maxSamples,$logFileName)
{
    Import-Module -Force ..\Networking\libNetworking.psm1
    $parentPath=(Split-Path $PSCommandPath -Parent)

    if($computers -eq $null -and $computersListFilePath -ne $null)
    {
        $computers=Get-Content $computersListFilePath   
    }

    $activeComputers=@()

    foreach($computer in $computers)
    {
        if((Test-Connection -ComputerName $computer -Count 1 -ErrorAction SilentlyContinue) -or ((Test-Port -computer $computer -port 135).Open -eq $true))
        {
            Assert-PerfmonServiceDependency -computerName $computer
            Write-Host "Adding $computer to list of active computers to monitor"
            $activeComputers+=$computer    
        }
        else
        {
            Write-Host "$computer will not be monitored"    
        }
    }

    foreach($template in $templates)
    {
        $counters+=Get-Content $parentPath\PerformanceCountersTemplates\$template.txt
    }
    $executionTime=(Get-Date).GetDateTimeFormats("u").Replace(":","")

    if($null -eq $logFilePath)
    {
        $logFilePath="$parentPath\PerfmonData\"
    }

    if($logFileName -eq $null)
    {
        $logFileFullPath="$logFilePath$executionTime`_$tag`_Capture.blg"
    }
    else
    {
        $logFileFullPath="$logFilePath$logFileName"   
    }

    if($outputType -ieq "log")
    {
        
        
        if($sampleInterval -eq $null -and $maxSamples -eq $null)
        {
            Get-Counter -ComputerName $activeComputers -Counter $counters -Continuous -ErrorAction SilentlyContinue | Export-Counter -Path $logFileFullPath -Force
        }
        else
        {
            Get-Counter -ComputerName $activeComputers -Counter $counters -SampleInterval $sampleInterval -MaxSamples $maxSamples -ErrorAction SilentlyContinue | Export-Counter -Path $logFileFullPath -Force
        }
    }
    elseif($outputType -ieq "stream")
    {
        if($sampleInterval -eq $null -and $maxSamples -eq $null)
        {
            Get-Counter -ComputerName $activeComputers -Counter $counters -Continuous -ErrorAction SilentlyContinue
        }
        else
        {
            Get-Counter -ComputerName $activeComputers -Counter $counters -SampleInterval $sampleInterval -MaxSamples $maxSamples -ErrorAction SilentlyContinue
        }

    }
    else
    {
        Get-Counter -ComputerName $activeComputers -Counter $counters -MaxSamples 2 | Export-Counter -Path $logFileFullPath -Force
        Start-Process $logFileFullPath
    }
    Write-Host "Monitoring has started"
}

<#
.DESCRIPTION
    Filter specific counter values from the pipeline.
.PARAMETER minimumValue
    The minimum value desired for the counter sample.    
.PARAMETER maximumValue
    The maximum value desired for the counter sample.
.PARAMETER counterPath
    The path of the counter on which the filter should be applied.
.PARAMETER instanceNameExclusion
    The name of a counter instance to exclude from the output.
.NOTES
    Author: Mathieu Isabel   
#>

filter Filter-CounterValues($minimumValue,$maximumValue,$counterPath,$instanceNameExclusion)
{
    Process
    {
        foreach($counterSample in $_.CounterSamples)
        {
            if($counterSample.Path -like "*$counterPath*")
            {
                if($counterSample.InstanceName -notlike $instanceNameExclusion)
                {
                    if($minimumValue -ne $null -and $maximumValue -ne $null)
                    {
                        if($counterSample.CookedValue -gt $minimumValue -and $counterSample.CookedValue -lt $maximumValue)
                        {
                            $counterSample
                        }
                    }
                    elseif($maximumValue -eq $null)
                    {
                        if($counterSample.CookedValue -gt $minimumValue)
                        {
                            $counterSample
                        }
                    }
                }
            }
        }
    }
}

<#
.DESCRIPTION
    Converts a counter sample to a hashtable.
.PARAMETER counterPath
    The path of the counter.
.PARAMETER thresholdValue
    The value of the threshold that was exceeded. #TODO would need to clean this up.
.NOTES
    Author: Mathieu Isabel   
#>
Function Convert-PerformanceCounterToHashTable($counterPath,$thresholdValue)
{
    Process
    {
        @{Path=$_.Path
        ComputerName=$_.Path.Split("\\")[2]
        CounterName=$counterPath
        InstanceName=$_.InstanceName
        SampleTime=$_.Timestamp
        CounterValue=$_.CookedValue
        ThresholdValue=$thresholdValue}    
    }
}

<#
.DESCRIPTION
    Persists a counter sample to a SQL Server database.
.PARAMETER counterPath
    The path of the counter.
.PARAMETER thresholdValue
    The value of the threshold that was exceeded. #TODO would need to clean this up.
.NOTES
    Author: Mathieu Isabel   
#>
Function Store-PerformanceCounter($counterPath,$thresholdValue)
{
    Begin
    {
        Import-Module -DisableNameChecking -Force ..\ConfigurationDatabase\libConfigurationDatabase.psm1
        $sqlConn = new-object ("Data.SqlClient.SqlConnection") (Get-ConfigurationSetting -environmentShortName $environmentShortName -scope "Global"  -name "VMWarehouseDatabaseConnectionString").Value

        $varcharDataType=[System.Data.SqlDbType]::VarChar
        $dateTimeDataType=[System.Data.SqlDbType]::DateTime
        $floatDataType=[System.Data.SqlDbType]::Float

        $insertCounterSampleQuery="INSERT INTO dbo.PerformanceCounterSamples(ComputerName,CounterName,InstanceName,SampleTime,CounterValue,ThresholdValue) 
                          VALUES(@ComputerName,@CounterName,@InstanceName,@SampleTime,@CounterValue,@ThresholdValue)"

        
        $computerNameParam = New-Object System.Data.SqlClient.SqlParameter "@ComputerName", $varcharDataType
        $counterNameParam = New-Object System.Data.SqlClient.SqlParameter "@CounterName", $varcharDataType
        $instanceNameParam = New-Object System.Data.SqlClient.SqlParameter "@InstanceName", $varcharDataType
        $sampleTimeParam = New-Object System.Data.SqlClient.SqlParameter "@SampleTime", $dateTimeDataType
        $counterValueParam = New-Object System.Data.SqlClient.SqlParameter "@CounterValue", $floatDataType
        $thresholdValueParam = New-Object System.Data.SqlClient.SqlParameter "@ThresholdValue", $floatDataType


        $insertCounterSampleCommand = New-Object Data.SqlClient.SqlCommand $insertCounterSampleQuery,$sqlConn
        $insertCounterSampleCommand.Parameters.Add($computerNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($counterNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($instanceNameParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($sampleTimeParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($counterValueParam) | Out-Null
        $insertCounterSampleCommand.Parameters.Add($thresholdValueParam) | Out-Null

        $sqlConn.Open()
    }
    Process
    {
        $path=$_.Path
        $computerNameParam.Value=$_.Path.Split("\\")[2]
        $counterNameParam.Value=$counterPath
        $instanceNameParam.Value=$_.InstanceName
        $sampleTimeParam.Value=$_.Timestamp
        $counterValueParam.Value=$_.CookedValue
        $thresholdValueParam.Value=$thresholdValue
        
        try
        {
            $insertCounterSampleCommand.ExecuteNonQuery() | OUt-Null
            Write-Output $_
        }
        catch
        {
            $_.Message
        }
    }
    End
    {
        $sqlConn.Dispose()
    }   
}

<#
.DESCRIPTION
    Execute logic when a counter sample is received on the pipeline.
.PARAMETER counterSample
    The counter sample.
.NOTES
    Author: Mathieu Isabel   
#>
Function Execute-CounterTrigger
{
    param([Parameter(Mandatory=$true, 
                     ValueFromPipeline=$true)]
                     $counterSample)

    Process
    {
        if($counterSample.Path -like "*\Processor(_Total)\% Processor Time")
        {
            
            $computerName=($counterSample.Path.Replace("\\","\") -split '\\')[1]
            $processesStatistics=get-wmiobject Win32_PerfFormattedData_PerfProc_Process -ComputerName $computerName -ErrorAction SilentlyContinue | where {$_.PercentProcessorTime -gt 10 -and $_.Name -ne "_Total" -and $_.Name -ne "Idle"} | select Name,PercentProcessorTime,WorkingSetPrivate,IDProcess

            foreach($processStatistic in $processesStatistics)
            {
                if($processStatistic.Name -ilike "*svchost*")
                {
                    $svcHostInformation=Get-WmiObject -Class Win32_Service -ComputerName $computerName -ErrorAction SilentlyContinue | Where-Object {$_.ProcessId -eq $processStatistic.IDProcess}
                    $processName=$svcHostInformation.DisplayName -join "|"
                }
                else
                {
                    $processName=$processStatistic.Name
                    if($processName -ilike "*#*")
                    {
                        $processName=$processName.Substring(0,$processName.IndexOf("#",0))
                    }
                }
                
                New-Object PSObject -Property @{SampleTime=Get-Date;
                                                ComputerName=$computerName;
                                                Id=$processStatistic.IDProcess;
                                                Name=$processName;
                                                PercentProcessorTime=$processStatistic.PercentProcessorTime;
                                                MemoryInMB=$processStatistic.WorkingSetPrivate/1MB}
            }

        }
        else
        {
            
        }
    }    
}

Function Get-PerformanceMonitoringSplitData($computers,$computersListFilePath,$templates,[switch]$persistPerformanceData,$tag)
{
    if($computers -eq $null -and $computersListFilePath -ne $null)
    {
        $computers=Get-Content $computersListFilePath   
    }

    foreach($template in $templates)
    {
        $counters+=Get-Content .\PerformanceCountersTemplates\$template.txt
    }
    

    $executionTime=(Get-Date).GetDateTimeFormats("u").Replace(":","")
    ForEach($computer in $computers)
    {
        $command={Get-Counter -ComputerName $computer -Counter $counters -Continuous | Export-Counter -Path c:\Code\Windows\PerfmonData\$executionTime`_$computer`_Capture.blg -Force }
        Start-Job -ScriptBlock $command -ArgumentList $computer,$counters,$executionTime -Name "Monitor $computer"
    }
    

    
}


<#
.DESCRIPTION
    Gather basic statistics from a perfmon counter log files.
.PARAMETER perfmonLogPath
    The path of the perfmon log file.
.PARAMETER counterPath
    The path of the counter analyze.
.PARAMETER counterSet
    The name of the counter set that will be analyzed.
.PARAMETER counterMinimumValue
    The minimum counter value to include in the analysis.
.PARAMETER counterMaximumValue
    The maximum counter value to include in the analysis.
.PARAMETER startTime
    The start datetime to include in the analysis.
.PARAMETER endTime
    The stop datetime to include in the analysis.
.NOTES
    Author: Mathieu Isabel   
#>
Function Get-CounterStatistics($perfmonLogPath,$counterPath,$counterSet,$counterMinimumValue,$counterMaximumValue,$startTime,$endTime)
{
    <#
    $log=Import-Counter -Path $perfmonLogPath -ErrorAction SilentlyContinue -StartTime $startTime -EndTime $endTime -Counter $counterPath    
    
    
    if($counterMinimumValue -ne $null -and $counterMaximumValue -ne $null)
    {
        $queryResults=$log.CounterSamples | where { $_.CookedValue -gt $counterMinimumValue -and $_.CookedValue -lt $counterMaximumValue } | select TimeStamp,Path,CookedValue
    }
    elseif($counterMinimumValue -ne $null -and $counterMaximumValue -eq $null)
    {
        $queryResults=$log.CounterSamples | where { $_.CookedValue -gt $counterMinimumValue } | select TimeStamp,Path,CookedValue
    }
    elseif($counterMinimumValue -eq $null -and $counterMaximumValue -eq $null)
    {
        $queryResults=$log.CounterSamples | select TimeStamp,Path,CookedValue
    }
    #>
    $counters= (Import-Counter -Path $perfmonLogPath -ListSet $counterSet).PathsWithInstances | Where-Object {$_ -like $counterPath}

    $countersSummaries=@()
    
    foreach ($counter in $counters)
    {
        #$counterMeasures=$queryResults | Where-Object {$_.Path -eq $counter} | Measure-Object -Sum -Minimum -Maximum -Average -Property CookedValue
        $counterMeasures=(Import-Counter -Path $perfmonLogPath -ErrorAction SilentlyContinue -StartTime $startTime -EndTime $endTime -Counter $counter).CounterSamples | Measure-Object -Sum -Minimum -Maximum -Average -Property CookedValue
        $countersSummaries+=New-Object PSObject -Property @{Counter=$counter;
                                                            Count=$counterMeasures.Count;
                                                            Minimum=$counterMeasures.Minimum;
                                                            Maximum=$counterMeasures.Maximum;
                                                            Average=$counterMeasures.Average;
                                                            Sum=$counterMeasures.Sum;
                                                            }
    }
    



    $countersSummaries
}

Function Get-CounterInstanceFromTemplate($computerName,$instanceNames=$null,$counterCategory,$templates)
{
    $path=(Split-Path $PSCommandPath -Parent)

    foreach($template in $templates)
    {
        $counters+=Get-Content $path\PerformanceCountersTemplates\$template.txt
    }
    
    $monitoredCounters=@()

    $countersInstance=(Get-Counter -ComputerName $computerName -ListSet "*$counterCategory*").PathsWithInstances
    
    foreach($instanceName in $instanceNames)
    {
        foreach($counter in $counters)
        {
            $instanceCounter=$countersInstance | Where-Object {$_ -like "*" + $counter.Replace('*',$instanceName.ToString()+'*')}
            $monitoredCounters+=$instanceCounter
        }
    }
    
    $monitoredCounters
}

Function Assert-PerfmonServiceDependency($computerName)
{
    $parentPath=(Split-Path $PSCommandPath -Parent)
    Import-Module -Force $parentPath\libWindowsService.psm1
    
    Assert-ServiceState -serviceName RemoteRegistry -computerName $computerName
    Assert-ServiceState -serviceName pla -computerName $computerName
    
}

Function Convert-PerfmonCSVData($logFilePath)
{
    $sourceData=Get-Content -Path $logFilePath
    $convertedLine=New-Object -TypeName PSObject 
    $convertedLine | Add-Member -MemberType NoteProperty -Name ComputerName -Value $null
    $convertedLine | Add-Member -MemberType NoteProperty -Name SampleTime -Value $null
    $convertedLine | Add-Member -MemberType NoteProperty -Name CounterCategory -Value $null
    $convertedLine | Add-Member -MemberType NoteProperty -Name CounterName -Value $null
    $convertedLine | Add-Member -MemberType NoteProperty -Name InstanceName -Value $null
    $convertedLine | Add-Member -MemberType NoteProperty -Name CounterValue -Value $null

    $columnTitles=$sourceData[0].Split(",")

    for($i=1;$i -lt $sourceData.Count;$i++)
    {
        if($sourceData[$i] -ne $null -and $sourceData[$i] -ne "")
        {
            $columns=$sourceData[$i].Split(",")

            $sampleTime=$columns[0].Replace('"',"")
        
            for($j=2;$j -lt $columns.Count;$j++)
            {
                $computerName=$columnTitles[$j].Replace('"',"").Substring(2,$columnTitles[$j].IndexOf("\\",2)-3)
                $counterCategory=$columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[0].Substring(0,$columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[0].IndexOf("("))
                $counterName=$columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[1]
                $instanceName=$columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[0].Substring($columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[0].IndexOf("(")+1,$columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[0].IndexOf(")")-$columnTitles[$j].Replace('"',"").Replace("\\$computerName\\","").Split("\")[0].IndexOf("(")-1)
                $counterValue=$columns[$j].Replace('"',"")
                
                $convertedLine.ComputerName=$computerName
                $convertedLine.SampleTime=$sampleTime
                $convertedLine.CounterCategory=$counterCategory
                $convertedLine.CounterName=$counterName
                $convertedLine.InstanceName=$instanceName
                $convertedLine.CounterValue=$counterValue
                $convertedLine
            }
        }
    }
}

Function Convert-PerfmonBinaryData($logFilePath)
{
    begin
    {
        $convertedLine=New-Object -TypeName PSObject 
        $convertedLine | Add-Member -MemberType NoteProperty -Name ComputerName -Value $null
        $convertedLine | Add-Member -MemberType NoteProperty -Name SampleTime -Value $null
        $convertedLine | Add-Member -MemberType NoteProperty -Name CounterCategory -Value $null
        $convertedLine | Add-Member -MemberType NoteProperty -Name CounterName -Value $null
        $convertedLine | Add-Member -MemberType NoteProperty -Name InstanceName -Value $null
        $convertedLine | Add-Member -MemberType NoteProperty -Name CounterValue -Value $null
        $counterSamples=$null
        $sampleTime=$null
        $computerName=$null
        $counterCategory=$null
        $counterName=$null
        $instanceName=$null
        $counterValue=$null
    }
    process
    {
        $counterSamples=$_.CounterSamples
        $sampleTime=$_.Timestamp
        
        foreach($counterSample in $counterSamples)
        {
            $computerName=$counterSample.Path.Substring(2,$counterSample.Path.IndexOf("\\",2)-2)
            $counterCategory=$counterSample.Path.Replace("\\$computerName\\","").Split("\")[0].SubString(0,$counterSample.Path.Replace("\\$computerName\\","").Split("\")[0].IndexOf("("))
            $counterName=$counterSample.Path.Replace("\\$computerName\\","").Split("\")[1]
            $instanceName=$counterSample.InstanceName
            $counterValue=$counterSample.CookedValue
            
            $convertedLine.ComputerName=$computerName
            $convertedLine.SampleTime=$sampleTime
            $convertedLine.CounterCategory=$counterCategory
            $convertedLine.CounterName=$counterName
            $convertedLine.InstanceName=$instanceName
            $convertedLine.CounterValue=$counterValue
            $convertedLine
        }
    }
}

Function Get-CounterInstances($computerNames,$counterPathFilter,$logFilePath)
{
    $counterInstances=(Import-Counter -Path $logFilePath -ListSet *).PathsWithInstances | Where-Object {$_ -like $counterPathFilter}

    $counterInstances
}

Function Import-FilteredCounter($logFilePath,$counterPathFilter,$computerNames)
{
    $counterInstances=Get-CounterInstances -computerNames $computerNames -counterPathFilter $counterPathFilter -logFilePath $logFilePath

    Import-Counter -Path $logFilePath -Counter $counterInstances
}

Function Parse-CounterSample($sampleTime)
{
    begin
    {
        
    }
    process
    {
        $counterSample=$_
        $parsedSample=New-Object -TypeName PSObject

        $parsedSample | Add-Member -MemberType NoteProperty -Name ComputerName -Value $counterSample.Path.Substring(2,$counterSample.Path.IndexOf("\\",2)-2)
        $parsedSample | Add-Member -MemberType NoteProperty -Name CounterCategory -Value $counterSample.Path.Replace("\\"+$parsedSample.ComputerName+"\\","").Split("\")[0].SubString(0,$counterSample.Path.Replace("\\"+$parsedSample.ComputerName+"\\","").Split("\")[0].IndexOf("("))   
        $parsedSample | Add-Member -MemberType NoteProperty -Name CounterName -Value $counterSample.Path.Replace("\\"+$parsedSample.ComputerName+"\\","").Split("\")[1]
        $parsedSample | Add-Member -MemberType NoteProperty -Name CounterInstanceName -Value $counterSample.InstanceName
        $parsedSample | Add-Member -MemberType NoteProperty -Name CounterValue -Value $counterSample.CookedValue
        $parsedSample | Add-Member -MemberType NoteProperty -Name SampleTime -Value $sampleTime
    
        $parsedSample
    }  
}